package com.lobov.utils;

import com.lobov.containers.MultiPair;
import com.lobov.entities.Matrix;
import com.lobov.entities.Snip;
import com.lobov.entities.yale.MatrixYale;
import com.lobov.entities.yale.SnipYale;
import com.lobov.entities.yale.SparseVector;

public class Compressor {

	/**
	 * Утилитарный метод для приведения матрицы в сжатую форму
	 * 
	 * @param matrix
	 *            получает разреженную матрицу
	 * @return Возвращает сжатую матрицу
	 * @throws Exception
	 *             Если матрица уже сжата выбрасывается исключение
	 */
	public static Matrix compressMatrix(Matrix matrix) throws Exception {
		if (matrix.isCompressed()) {
			throw new Exception("Матрица уже сжата!");
		}
		int xLength = matrix.getXlength();
		int yLength = matrix.getYlength();
		double[] oldvalues = matrix.getValues();

		int numOfNewValues = 0;

		// цикл для подсчета непустых значений
		for (int numOfOldValue = 0; numOfOldValue < oldvalues.length; numOfOldValue++) {
			if (oldvalues[numOfOldValue] != 0d) {
				numOfNewValues++;
			}
		}

		Matrix newMatrix = new Matrix(numOfNewValues, xLength, yLength);
		double[] newValues = newMatrix.getValues();
		int[] newXs = newMatrix.getXs();
		int[] newYs = newMatrix.getYs();

		int counterOfOldValues = 0;
		int counterOfNewValues = 0;
		// циклы для заполнения структуры сжатой матрицы
		for (int y = 0; y < yLength; y++) {
			for (int x = 0; x < xLength; x++) {
				if (oldvalues[counterOfOldValues] != 0) {
					newValues[counterOfNewValues] = oldvalues[counterOfOldValues];
					newXs[counterOfNewValues] = x;
					newYs[counterOfNewValues] = y;
					counterOfNewValues++;
				}
				counterOfOldValues++;
			}
		}
		newMatrix.setValues(newValues);
		newMatrix.setXs(newXs);
		newMatrix.setYs(newYs);

		return newMatrix;
	}

	/**
	 * Утилитарный метод для приведения матрицы в разреженную форму
	 * 
	 * @param matrix
	 *            получает сжатую матрицу
	 * @return Возвращает разреженную матрицу
	 * @throws Exception
	 *             Если матрица уже разреженная выбрасывается исключение
	 */
	public static Matrix deCompressMatrix(Matrix matrix) throws Exception {
		if (matrix.isCompressed()) {
			throw new Exception("Матрица уже разрежена!");
		}
		int xLength = matrix.getXlength();
		int yLength = matrix.getYlength();
		int[] oldXs = matrix.getXs();
		int[] oldYs = matrix.getYs();
		double[] oldvalues = matrix.getValues();

		Matrix newMatrix = new Matrix(xLength, yLength);
		double[] newValues = newMatrix.getValues();
		int[] newXs = newMatrix.getXs();
		int[] newYs = newMatrix.getYs();

		// установка значений из сжатой матрицы в массивы разреженной матрицы
		for (int i = 0; i < oldvalues.length; i++) {
			int settingPosition = getPosition(oldXs[i], oldYs[i], xLength);
			newValues[settingPosition] = oldvalues[i];
			newXs[settingPosition] = oldXs[i];
			newYs[settingPosition] = oldYs[i];
		}
		newMatrix.setValues(newValues);
		newMatrix.setXs(newXs);
		newMatrix.setYs(newYs);

		return newMatrix;
	}

	/**
	 * Утилитарный метод для приведения лоскута матрицы в сжатую форму
	 * 
	 * @param snip
	 *            получает разреженный лоскут
	 * @return Возвращает сжатый лоскут
	 * @throws Exception
	 *             Если лоскут уже сжат выбрасывается исключение
	 */
	public static Snip compressSnip(Snip snip) throws Exception {
		if (snip.isCompressed()) {
			throw new Exception("Лоскут уже сжат!");
		}
		int xLength = snip.getXlength();
		int yLength = snip.getYlength();
		double[] oldvalues = snip.getValues();

		int numOfNewValues = 0;

		// цикл для подсчета непустых значений
		for (int numOfOldValue = 0; numOfOldValue < oldvalues.length; numOfOldValue++) {
			if (oldvalues[numOfOldValue] != 0d) {
				numOfNewValues++;
			}
		}

		Snip newSnip = new Snip(numOfNewValues, snip.getNumOfStartElement(),
				xLength, yLength, snip.getOrderNumInContainer());
		double[] newValues = newSnip.getValues();
		int[] newXs = newSnip.getXs();
		int[] newYs = newSnip.getYs();

		int counterOfOldValues = 0;
		int counterOfNewValues = 0;
		// циклы для заполнения структуры сжатого лоскута
		for (int y = 0; y < yLength; y++) {
			for (int x = 0; x < xLength; x++) {
				if (oldvalues[counterOfOldValues] != 0) {
					newValues[counterOfNewValues] = oldvalues[counterOfOldValues];
					newXs[counterOfNewValues] = x;
					newYs[counterOfNewValues] = y;
					counterOfNewValues++;
				}
				counterOfOldValues++;
			}
		}
		newSnip.setValues(newValues);
		newSnip.setXs(newXs);
		newSnip.setYs(newYs);

		return newSnip;
	}

	/**
	 * Утилитарный метод для приведения лоскута йельской матрицы в сжатую форму
	 * 
	 * @param snip
	 *            получает разреженный йельской лоскут
	 * @return Возвращает сжатый лоскут
	 * @throws Exception
	 *             Если лоскут уже сжат выбрасывается исключение
	 */
	public static SnipYale compressSnipYale(SnipYale snip) throws Exception {
		if (snip.isCompressed()) {
			throw new Exception("Лоскут уже сжат!");
		}
		
		SparseVector[] vectors = snip.getVectors();
		for(int i = 0; i < vectors.length; i++){
			vectors[i].compress();
		}
		snip.setCompressed(true);
		return snip;
	}

	
	
	/**
	 * Утилитарный метод для приведения лоскута в разреженную форму
	 * 
	 * @param snip
	 *            получает сжатый лоскут
	 * @return Возвращает разреженный лоскут
	 * @throws Exception
	 *             Если лоскут уже разреженный выбрасывается исключение
	 */
	public static Snip deCompressSnip(Snip snip) throws Exception {
		if (snip.isCompressed()) {
			throw new Exception("Лоскут уже разрежен!");
		}
		int xLength = snip.getXlength();
		int yLength = snip.getYlength();
		int[] oldXs = snip.getXs();
		int[] oldYs = snip.getYs();
		double[] oldvalues = snip.getValues();

		Snip newSnip = new Snip(snip.getNumOfStartElement(), xLength, yLength,
				snip.getOrderNumInContainer());
		double[] newValues = newSnip.getValues();
		int[] newXs = newSnip.getXs();
		int[] newYs = newSnip.getYs();

		// установка значений из сжатого лоскута в массивы разреженного лоскута
		for (int i = 0; i < oldvalues.length; i++) {
			int settingPosition = getPosition(oldXs[i], oldYs[i], xLength);
			newValues[settingPosition] = oldvalues[i];
			newXs[settingPosition] = oldXs[i];
			newYs[settingPosition] = oldYs[i];
		}
		newSnip.setValues(newValues);
		newSnip.setXs(newXs);
		newSnip.setYs(newYs);

		return newSnip;
	}

	public static MultiPair compressMultipair(MultiPair multiPair)
			throws Exception {
		Matrix matrix = multiPair.getM1();
		matrix = compressMatrix(matrix);

		Snip snip = multiPair.getSnip();
		snip = deCompressSnip(snip);

		multiPair.setM1(matrix);
		multiPair.setSnip(snip);

		return multiPair;
	}

	private static int getPosition(int x, int y, int length) {
		return length * y + x;
	}

	/**
	 * Утилитарный метод для приведения вектора в сжатую форму
	 * 
	 * @param vector
	 *            - разреженный вектор
	 * @return
	 * @throws Exception
	 *             Если вектор уже сжат выбрасывается исключение
	 */
	public static SparseVector compressSparseVector(SparseVector vector)
			throws Exception {
		if (vector.isCompressed()) {
			throw new Exception("Вектор уже сжат!");
		}
		int xLength = vector.getxLength();
		double[] oldvalues = vector.getValues();

		int numOfNewValues = 0;

		// цикл для подсчета непустых значений
		for (int numOfOldValue = 0; numOfOldValue < oldvalues.length; numOfOldValue++) {
			if (oldvalues[numOfOldValue] != 0d) {
				numOfNewValues++;
			}
		}

		int[] xs = new int[numOfNewValues];
		double[] newValues = new double[numOfNewValues];

		numOfNewValues = 0;

		// и запись их и их позиций в массивы
		for (int numOfOldValue = 0; numOfOldValue < oldvalues.length; numOfOldValue++) {
			if (oldvalues[numOfOldValue] != 0d) {
				xs[numOfNewValues] = numOfOldValue;
				newValues[numOfNewValues] = oldvalues[numOfOldValue];
				numOfNewValues++;
			}
		}

		SparseVector newVector = new SparseVector(xLength,
				vector.getyPosition(), newValues, xs, true);
		return newVector;
	}

	public static MatrixYale compressMatrixYale(MatrixYale matrixYale)
			throws Exception {
		MatrixYale newMatrixYale = new MatrixYale(matrixYale.getXlength(),
				matrixYale.getYlength());
		SparseVector[] newVectors = new SparseVector[matrixYale.getVectors().length];
		SparseVector[] oldVectors = matrixYale.getVectors();
		for (int y = 0; y < matrixYale.getYlength(); y++) {
			newVectors[y] = (SparseVector) oldVectors[y].compress();
		}
		newMatrixYale.setVectors(newVectors);
		newMatrixYale.setCompressed(true);
		return newMatrixYale;
	}

}
